import os
import sys
import dash
from pyecharts import options as opts
from dash import dcc, html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import networkx as nx
from pyecharts.charts import Map
from functools import lru_cache
import concurrent.futures
from collections import defaultdict

province_mapping = {
    '北京': '北京市',
    '天津': '天津市',
    '河北': '河北省',
    '山西': '山西省',
    '内蒙古': '内蒙古自治区',
    '辽宁': '辽宁省',
    '吉林': '吉林省',
    '黑龙江': '黑龙江省',
    '上海': '上海市',
    '江苏': '江苏省',
    '浙江': '浙江省',
    '安徽': '安徽省',
    '福建': '福建省',
    '江西': '江西省',
    '山东': '山东省',
    '河南': '河南省',
    '湖北': '湖北省',
    '湖南': '湖南省',
    '广东': '广东省',
    '广西': '广西壮族自治区',
    '海南': '海南省',
    '重庆': '重庆市',
    '四川': '四川省',
    '贵州': '贵州省',
    '云南': '云南省',
    '西藏': '西藏自治区',
    '陕西': '陕西省',
    '甘肃': '甘肃省',
    '青海': '青海省',
    '宁夏': '宁夏回族自治区',
    '新疆': '新疆维吾尔自治区',
    '台湾': '台湾省',
    '香港': '香港特别行政区',
    '澳门': '澳门特别行政区'
}


def extract_nature_words(text):
    if pd.isna(text):
        return []
    nature_words = ['山', '风', '日', '云', '水', '花', '月', '江', '雨', '草', '马', '海']
    stop_words = {'的', '了', '是', '在', '和', '也', '不', '有', '我', '你', '他', '她', '它', '这', '那'}
    result = []
    for word in nature_words:
        if word in text and word not in stop_words:
            count = text.count(word)
            result.extend([word] * count)
    return result


def extract_and_map_provinces(df):
    df['现代地址'] = df['现代地址'].astype(str)

    def extract_province(address):
        for province in province_mapping.keys():
            if province in address:
                return province
        return None

    df['省份'] = df['现代地址'].apply(extract_province)
    valid_provinces = set(province_mapping.keys())
    df = df[df['省份'].isin(valid_provinces)]

    return df, province_mapping


class DataCache:
    def __init__(self):
        self.cache = None
        self.hash_value = None

    def read_data(self):
        try:
            if getattr(sys, 'frozen', False):
                base_path = sys._MEIPASS
            else:
                base_path = os.path.dirname(os.path.abspath(__file__))

            poems_path = os.path.join(base_path, 'dispose_data', 'poems.csv')
            result_path = os.path.join(base_path, 'dispose_data', 'result.csv')

            poems_df = pd.read_csv(poems_path, encoding='utf-8')
            result_df = pd.read_csv(result_path, encoding='gbk')

            poems_df.columns = poems_df.columns.str.strip()
            result_df.columns = result_df.columns.str.strip()

            merged_df = pd.merge(
                poems_df.rename(columns={'古诗名': '诗词名称'}),
                result_df.rename(columns={'性別': '性别'}),
                left_on='作者',
                right_on='姓名',
                how='left'
            )

            merged_df = merged_df.drop_duplicates(subset=['作者', '诗词名称'])

            required_columns = ['作者', '性别', '古代地址', '现代地址', '诗词名称', '诗词内容']
            for col in required_columns:
                if col not in merged_df:
                    merged_df[col] = '未知'

            merged_df['性别'] = merged_df['性别'].fillna('未知').map({'男': '男', '女': '女'}).fillna('未知')
            merged_df['古代地址'] = merged_df['古代地址'].fillna('地址不详')
            merged_df['现代地址'] = merged_df['现代地址'].fillna('地址不详')

            merged_df["自然词汇"] = merged_df["诗词内容"].apply(extract_nature_words)

            merged_df, _ = extract_and_map_provinces(merged_df)

            self.cache = merged_df
            return merged_df

        except Exception as e:
            print(f"数据处理最终错误: {str(e)}")
            return None


data_cache = DataCache()


# 生成词云图
@lru_cache(maxsize=32)
def generate_wordcloud(word_freq_str):
    word_freq = pd.read_json(word_freq_str)
    if word_freq.empty:
        return go.Figure()
    x_min, x_max = -1, 1
    y_min, y_max = -1, 1
    num_words = len(word_freq)
    x_mean, x_std = 0, 0.3
    y_mean, y_std = 0, 0.3
    x = np.random.normal(x_mean, x_std, num_words)
    y = np.random.normal(y_mean, y_std, num_words)
    x = np.clip(x, x_min, x_max)
    y = np.clip(y, y_min, y_max)
    word_freq = word_freq.sort_values(by='频次', ascending=False)
    max_font_size = 300
    word_freq['font_size'] = np.clip(word_freq['频次'] / 100, 10, max_font_size)
    random_factors = (np.random.rand(num_words) - 0.5) * (word_freq['font_size'] / 100)
    x += random_factors
    y += random_factors
    x = np.clip(x, x_min, x_max)
    y = np.clip(y, y_min, y_max)
    colors = ['#FF5733', '#33FF57', '#5733FF', '#FF33E7', '#33E7FF']
    word_freq['color'] = np.random.choice(colors, num_words)
    fig = go.Figure()
    for i, row in word_freq.iterrows():
        fig.add_trace(go.Scatter(
            x=[x[i]],
            y=[y[i]],
            mode='text',
            text=[row['词汇']],
            textfont=dict(
                size=row['font_size'],
                color=row['color'],
                family='SimHei'
            ),
            hoverinfo='text',
            hovertext=[f"{row['词汇']}: {row['频次']}"]
        ))
    fig.update_layout(
        title={
            "text": '唐代诗词自然意象词云图',
            "x": 0.5
        },
        xaxis=dict(showgrid=False, zeroline=False, showticklabels=False, range=[x_min, x_max]),
        yaxis=dict(showgrid=False, zeroline=False, showticklabels=False, range=[y_min, y_max]),
        hovermode='closest',
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85)
    )
    return fig


# 生成热力图
@lru_cache(maxsize=32)
def generate_heatmap(df_str):
    df = pd.read_json(df_str)
    if '现代地址' not in df.columns:
        print("合并后的数据中不存在 '现代地址' 列，请检查数据。")
        return go.Figure()
    heatmap_data = []
    for _, row in df.iterrows():
        for word in row['自然词汇']:
            heatmap_data.append({'省份': row['省份'], '自然意象': word})
    heatmap_df = pd.DataFrame(heatmap_data)
    heatmap_pivot = heatmap_df.groupby(['省份', '自然意象']).size().reset_index(name='频次')
    custom_colors = ['#FF0000', '#FFFF00', '#00FF00']
    fig = px.density_heatmap(
        heatmap_pivot,
        x='自然意象',
        y='省份',
        z='频次',
        color_continuous_scale=custom_colors,
        hover_data=['频次']
    )
    min_freq = heatmap_pivot['频次'].min()
    max_freq = heatmap_pivot['频次'].max()
    fig.update_coloraxes(cmin=min_freq, cmax=max_freq)
    fig.update_layout(
        font=dict(family='SimHei'),
        xaxis_title='自然意象',
        yaxis_title='诗人籍贯省份',
        hovermode='closest',
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        title={
            "text": '唐代诗人籍贯省份与自然意象关联热力图',
            "x": 0.5
        }
    )

    return fig


# 生成地图
@lru_cache(maxsize=32)
def generate_map(df_str):
    df = pd.read_json(df_str)
    if '现代地址' not in df.columns:
        print("合并后的数据中不存在 '现代地址' 列，请检查数据。")
        return html.Div("数据中缺少 '现代地址' 列")
    df['省份'] = df['现代地址'].astype(str).apply(lambda x: next((p for p in province_mapping.keys() if p in x), None))
    df = df.dropna(subset=['省份'])
    province_count = df.groupby('省份').size().reset_index(name='诗人数量')
    province_count['省份全称'] = province_count['省份'].map(province_mapping)
    all_provinces = pd.DataFrame({
        '省份全称': list(province_mapping.values()),
        '诗人数量': 0
    })
    merged_data = pd.merge(all_provinces, province_count, on='省份全称', how='left')
    merged_data['诗人数量_y'].fillna(0, inplace=True)
    merged_data['诗人数量'] = merged_data['诗人数量_y']
    merged_data.drop(columns=['诗人数量_y'], inplace=True)
    data = []
    for _, row in merged_data.iterrows():
        data.append((row['省份全称'], row['诗人数量']))
    map_chart = (
        Map()
            .add("唐代诗人分布", data, "china")
            .set_global_opts(
            title_opts=opts.TitleOpts(
                title="唐代诗人分布地图",
                pos_left="center",
                pos_top="5%"
            ),
            visualmap_opts=opts.VisualMapOpts(
                min_=0,
                max_=merged_data['诗人数量'].max(),
                range_color=["yellow", "orange", "red"],
            )
        )
    )
    map_html = map_chart.render_embed()
    return html.Div([
        html.Div(
            html.Iframe(
                srcDoc=map_html,
                style={
                    "width": "100%",
                    "height": "100%",
                    "border": "none",
                    "margin": "0",
                    "padding": "0"
                }
            ),
            style={
                "width": 1440,
                "height": 810,
                "marginLeft": 300,
                "marginRight": 200,
                "marginTop": 0,
                "marginBottom": 85
            }
        )
    ])


# 生成雷达图
def generate_radar(selected_poet=None):
    df = data_cache.read_data()
    if df is None:
        return go.Figure()
    if selected_poet:
        df = df[df['作者'].isin(selected_poet)]
    emotion_dict = {
        '山': 0.8,
        '风': 0.4,
        '日': 0.7,
        '云': 0.6,
        '水': 0.6,
        '花': 0.9,
        '月': 0.7,
        '江': 0.6,
        '雨': 0.3,
        '草': 0.4,
        '马': 0.7,
        '海': 0.8,
    }
    all_nature_words = [word for sublist in df['自然词汇'] for word in sublist]
    word_counts = pd.Series(all_nature_words).value_counts().reset_index()
    word_counts.columns = ['词汇', '频次']
    scores = []
    for word in emotion_dict.keys():
        count = word_counts[word_counts['词汇'] == word]['频次'].values[0] if word in word_counts['词汇'].values else 0
        scores.append(count * emotion_dict[word])
    labels = list(emotion_dict.keys())
    scores = [s / sum(scores) if sum(scores) > 0 else 0 for s in scores]
    labels += labels[:1]
    scores += scores[:1]
    angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False).tolist()
    fig = go.Figure()
    fig.add_trace(go.Scatterpolar(
        r=scores,
        theta=labels,
        fill='toself',
        name='情感得分',
        hoverinfo='text',
        hovertext=[f"{label}: {score}" for label, score in zip(labels[:-1], scores[:-1])]
    ))
    fig.update_layout(
        polar=dict(
            radialaxis=dict(
                visible=True,
                range=[0, 1]
            )
        ),
        title={
            "text": f'{selected_poet if selected_poet else "唐代"}自然意象情感分析雷达图',
            "x": 0.5
        },
        font=dict(family='SimHei'),
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        hovermode='closest'
    )
    return fig


# 生成关系网络图
def _generate_network_graph(df):
    nature_words = ['山', '风', '日', '云', '水', '花', '月', '江', '雨', '草', '马', '海']
    co_occurrence = defaultdict(int)
    for words in df['自然词汇']:
        for i in range(len(words)):
            for j in range(i + 1, len(words)):
                word1 = words[i]
                word2 = words[j]
                if word1 in nature_words and word2 in nature_words:
                    pair = tuple(sorted([word1, word2]))
                    co_occurrence[pair] += 1
    G = nx.Graph()
    for pair, count in co_occurrence.items():
        node1, node2 = pair
        if not G.has_node(node1):
            G.add_node(node1)
        if not G.has_node(node2):
            G.add_node(node2)
        G.add_edge(node1, node2, weight=count)
    pos = nx.spring_layout(G)
    edge_data = []
    for edge in G.edges(data=True):
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        weight = edge[2]['weight']
        edge_data.append(([x0, x1], [y0, y1], weight))
    if not edge_data:
        print("未统计到自然意象的共现情况，生成空的关系网络图")
        return go.Figure()
    edge_traces = []
    for x, y, weight in edge_data:
        edge_trace = go.Scatter(
            x=x,
            y=y,
            mode='lines',
            line=dict(color='black', width=1)  # 设置边的颜色为黑色，宽度为1，可根据需要调整
        )
        edge_traces.append(edge_trace)
    node_x = []
    node_y = []
    node_text = []
    for node in G.nodes():
        x, y = pos[node]
        node_x.append(x)
        node_y.append(y)
        node_text.append(node)
    node_trace = go.Scatter(
        x=node_x,
        y=node_y,
        mode='markers+text',
        hoverinfo='text',
        text=node_text,
        textposition='top center',
        marker=dict(
            showscale=False,
            color='lightblue',
            size=50
        )
    )
    annotations = []
    for x, y, weight in edge_data:
        x_mid = (x[0] + x[1]) / 2
        y_mid = (y[0] + y[1]) / 2
        annotations.append(
            go.layout.Annotation(
                x=x_mid,
                y=y_mid,
                text=f'{weight}',
                showarrow=False,
                font=dict(size=10)
            )
        )
    fig = go.Figure(
        data=edge_traces + [node_trace],
        layout=go.Layout(
            title={
                "text": '唐代诗词自然意象关系网络图',
                "x": 0.5,
                "font": {
                    "size": 16
                }
            },
            showlegend=False,
            hovermode='closest',
            margin=dict(l=300, r=200, t=85, b=85),
            annotations=annotations,  # 添加边的标签到布局中
            xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
            yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
            font=dict(family='SimHei'),
            width=1440,
            height=810,
        )
    )
    return fig


def async_generate_network_graph(df_str):
    df = pd.read_json(df_str)
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future = executor.submit(_generate_network_graph, df)
        return future.result()


# 生成柱状图
@lru_cache(maxsize=32)
def generate_bar_chart(word_freq_str, selected_poet=None):
    word_freq = pd.read_json(word_freq_str)
    if word_freq.empty:
        return go.Figure()
    title = f'{selected_poet if selected_poet else "唐代"}诗词自然意象出现次数柱状图'
    fig = go.Figure(data=[go.Bar(
        x=word_freq['词汇'],
        y=word_freq['频次'],
        marker_color='rgb(158,202,225)',
        hoverinfo='y'
    )])
    fig.update_layout(
        title={
            "text": title,
            "x": 0.5
        },
        xaxis_title='自然意象',
        yaxis_title='出现次数',
        font=dict(family='SimHei'),
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        hovermode='closest'
    )
    return fig


# 生成箱线图
@lru_cache(maxsize=32)
def generate_box_plot(df_str):
    df = pd.read_json(df_str)
    emotion_dict = {
        '山': 0.8,
        '风': 0.4,
        '日': 0.7,
        '云': 0.6,
        '水': 0.6,
        '花': 0.9,
        '月': 0.7,
        '江': 0.6,
        '雨': 0.3,
        '草': 0.4,
        '马': 0.7,
        '海': 0.8,
    }
    data = []
    for word in emotion_dict.keys():
        data.append(df['自然词汇'].apply(lambda x: x.count(word) if isinstance(x, list) else 0))
    box_data = pd.DataFrame(data).transpose()
    box_data.columns = emotion_dict.keys()
    fig = go.Figure(data=[go.Box(
        y=box_data.values.T,
        name="自然意象",
        boxpoints='all',
        jitter=0.3,
        whiskerwidth=0.2,
        hoverinfo='y'
    )])
    fig.update_layout(
        title={
            "text": '唐代诗词自然意象情感得分箱线图',
            "x": 0.5
        },
        yaxis_title='出现次数',
        font=dict(family='SimHei'),
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        hovermode='closest'
    )
    return fig


# 生成桑葚图
@lru_cache(maxsize=32)
def generate_sankey_chart(df_str):
    df = pd.read_json(df_str)
    nature_words = ['山', '风', '日', '云', '水', '花', '月', '江', '雨', '草', '马', '海']
    node_list = nature_words + list(province_mapping.values())
    node_dict = {node: index for index, node in enumerate(node_list)}
    count_dict = df.explode('自然词汇').groupby(['省份', '自然词汇']).size().reset_index(name='count')
    count_dict['省份'] = count_dict['省份'].map(province_mapping)
    count_dict = count_dict.dropna()
    source = count_dict['自然词汇'].map(node_dict).tolist()
    target = count_dict['省份'].map(node_dict).tolist()
    value = count_dict['count'].tolist()
    link_colors = ['black'] * len(value)
    fig = go.Figure(data=[go.Sankey(
        node=dict(
            pad=2,
            thickness=5,
            line=dict(color="black", width=0.1),
            label=node_list
        ),
        link=dict(
            source=source,
            target=target,
            value=value,
            color=link_colors,
            hovertemplate='源: %{source.label}<br>目标: %{target.label}<br>出现次数: %{value}<extra></extra>'
        )
    )])
    fig.update_layout(
        title={
            "text": "唐代诗词自然意象与诗人籍贯省份关系桑基图",
            "x": 0.5,
            "font": {
                "family": "SimHei"
            }
        },
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        hovermode='closest'
    )
    return fig


# 生成旭日图
@lru_cache(maxsize=32)
def generate_sunburst_chart(df_str):
    df = pd.read_json(df_str)
    emotion_dict = {
        '山': 0.8,
        '风': 0.4,
        '日': 0.7,
        '云': 0.6,
        '水': 0.6,
        '花': 0.9,
        '月': 0.7,
        '江': 0.6,
        '雨': 0.3,
        '草': 0.4,
        '马': 0.7,
        '海': 0.8,
    }
    emotion_levels = {}
    for word, score in emotion_dict.items():
        if score >= 0.7:
            emotion_levels[word] = "积极"
        elif score >= 0.4:
            emotion_levels[word] = "中性"
        else:
            emotion_levels[word] = "消极"
    all_nature_words = [word for sublist in df['自然词汇'] for word in sublist]
    word_counts = pd.Series(all_nature_words).value_counts().reset_index()
    word_counts.columns = ['词汇', '频次']
    data = []
    for _, row in word_counts.iterrows():
        word = row['词汇']
        count = row['频次']
        emotion = emotion_levels.get(word, "未知")
        data.append({"自然意象": word, "情感程度": emotion, "频次": count})
    df_sunburst = pd.DataFrame(data)
    fig = px.sunburst(
        df_sunburst,
        path=["情感程度", "自然意象"],
        values="频次",
        title="唐代诗词自然意象情感层次旭日图",
        color_discrete_sequence=px.colors.qualitative.Set1,
        hover_data=['频次']
    )
    fig.update_layout(
        title={
            "text": "唐代诗词自然意象情感层次旭日图",
            "x": 0.5
        },
        font=dict(family="SimHei"),
        width=1440,
        height=810,
        margin=dict(l=300, r=200, t=85, b=85),
        hovermode='closest'
    )
    return fig


# 李白迁徙数据
def load_libai_data():
    try:
        if getattr(sys, 'frozen', False):
            base_path = sys._MEIPASS
        else:
            base_path = os.path.dirname(os.path.abspath(__file__))
        libai_csv_path = os.path.join(base_path, 'dispose_data', 'libai.csv')
        libai_df = pd.read_csv(libai_csv_path, encoding='utf-8')
        libai_df = libai_df.sort_values(by='时间').reset_index(drop=True)
        new_rows = []
        for i in range(len(libai_df) - 1):
            current = libai_df.iloc[i]
            next_row = libai_df.iloc[i + 1]
            current_dict = current.to_dict()
            current_dict['迁徙路径'] = ""
            new_rows.append(current_dict)
            steps = 1
            for j in range(1, steps + 1):
                ratio = j / (steps + 1)
                new_rows.append({
                    '诗人': current['诗人'],
                    '时间': current['时间'] + (next_row['时间'] - current['时间']) * ratio,
                    '古代地名': f"{current['古代地名']}→{next_row['古代地名']}",
                    '迁徙路径': f"{current['古代地名']}→{next_row['古代地名']}",
                    '经度': current['经度'] + (next_row['经度'] - current['经度']) * ratio,
                    '纬度': current['纬度'] + (next_row['纬度'] - current['纬度']) * ratio,
                    '现代省份': current['现代省份'],
                    '情感得分': current['情感得分'] * (1 - ratio) + next_row['情感得分'] * ratio,
                    '代表诗作': ''
                })
        last_point = libai_df.iloc[-1].to_dict()
        last_point['迁徙路径'] = ""
        new_rows.append(last_point)
        return pd.DataFrame(new_rows).sort_values(by='时间')
    except Exception as e:
        print(f"加载李白迁徙数据失败: {str(e)}")
        return None


# 迁徙图
def create_migration_figure(df, frame_index=0):
    if df is None:
        return go.Figure()
    times = sorted(df['时间'].unique())
    current_time = times[frame_index]
    frame_df = df[df['时间'] <= current_time]
    fig = px.line_geo(
        frame_df,
        lon='经度',
        lat='纬度',
        hover_name='古代地名',
        hover_data={
            '时间': ':.0f',
            '迁徙路径': True,
            '情感得分': ':.2f',
            '代表诗作': True
        },
        markers=True
    )
    fig.update_traces(
        line=dict(color='#e74c3c', width=3),
        marker=dict(
            size=10,
            color='#2ecc71',
            symbol='circle',
            line=dict(width=2, color='white')
        )
    )
    fig.update_geos(
        center=dict(lon=108, lat=34),
        projection_scale=4.2,
        lataxis_range=[31, 26],
        lonaxis_range=[95, 125],
        showcountries=True,
        countrycolor="gray"
    )
    fig.update_layout(
        margin={"r": 0, "t": 0, "l": 0, "b": 0},
        transition_duration=500,
        paper_bgcolor='rgba(0,0,0,0)'
    )
    return fig


chart_descriptions = {
    'wordcloud': '此词云图呈现了唐代诗词中自然意象的分布。通过统计诗词里自然词汇的出现频次，'
                 '选取高频词汇生成词云。词的大小直观反映其出现的频次，频次越高，词越大。',
    'heatmap': '此热力图展示了唐代诗人籍贯省份与自然意象的关联。统计各省份诗人作品中自然词汇的出现频次，'
               '以颜色深浅体现频次高低。颜色越深，表明该自然意象在对应省份诗人作品中的出现频次越高。',
    'map': '此地图呈现了唐代诗人的地域分布状况。基于诗人籍贯的统计数据，用颜色深浅表示各省份诗人数量。'
           '颜色越深，意味着该省份的诗人数量越多，有助于直观把握唐代诗人的地域集中程度。',
    'radar': '此雷达图用于分析唐代诗词中自然意象的情感得分。依据预先设定的情感词典，为每个自然意象赋予情感得分。'
             '雷达图的半径长度代表该自然意象在诗词中情感得分的相对高低，助力分析自然意象的情感倾向。',
    'network_graph': '此关系网络图展示了唐代诗词中自然意象间的共现关系。统计不同自然意象在同一诗词中同时出现的频次，'
                     '以节点代表自然意象，边表示共现关系,便于理解自然意象之间的关联。',
    'bar_chart': '此柱状图展示了唐代诗词中自然意象的出现次数。统计各自然意象在诗词中的总出现次数，'
                 '以柱状图呈现，柱子高度代表出现次数。通过柱子高度，可直观比较不同自然意象的出现频率。',
    'box_plot': '此箱线图展示了唐代诗词中自然意象情感得分的分布。根据预先设定的情感词典为自然意象赋予情感得分，'
                '箱线图呈现这些得分的中位数、四分位数和异常值等信息，有助于分析自然意象情感得分的分布特征。',
    'sankey_chart': '此桑基图呈现了唐代诗词中自然意象与诗人籍贯省份的关系。统计每个省份对应同一自然意象的出现频次，'
                    '以桑基图展示，不同流向的宽度表示数据量。图中左侧节点为自然意象，右侧节点为诗人籍贯省份.'
                    '通过观察桑基图，可直观了解哪些自然意象在哪些省份的诗人作品中更为常见，'
                    '洞察唐代诗词创作中自然意象的地域特色与文化差异。',
    'sunburst_chart': '此旭日图展示了唐代诗词中自然意象的情感层次。依据预先设定的情感词典，将自然意象分为积极、中性、消极三类情感程度。'
                      '旭日图中心代表情感程度，外层圆环为具体自然意象，扇形面积大小对应出现频次。'
                      '通过观察旭日图，能清晰了解唐代诗词中自然意象的情感分布，以及不同情感程度下各自然意象的相对重要性。',
    'migration': '此图展示了李白的生平迁徙路径。按照时间顺序，通过地理坐标标记李白在不同时期的地理位置，'
                 '并用线条连接形成迁徙轨迹。借助播放、暂停按钮和时间滑块，可查看不同时间点的迁徙情况，'
                 '还能了解每个地点对应的时间、情感得分和代表诗作等信息。此图有助于直观了解李白一生的行踪，'
                 '以及他在不同地域创作时可能受到的环境影响。'
}
modal_styles = {
    'display': 'none',
    'position': 'fixed',
    'top': '50%',
    'left': '50%',
    'transform': 'translate(-50%, -50%)',
    'backgroundColor': 'white',
    'padding': '20px',
    'borderRadius': '10px',
    'boxShadow': '0 2px 8px rgba(0,0,0,0.1)',
    'zIndex': '1000',
    'width': '80%',
    'maxWidth': '800px'
}
close_button_styles_modal = {
    'position': 'absolute',
    'top': '-8px',
    'right': '-8px',
    'width': '24px',
    'height': '24px',
    'borderRadius': '50%',
    'backgroundColor': 'rgba(209, 0, 0, 0.5)',
    'display': 'flex',
    'justifyContent': 'center',
    'alignItems': 'center',
    'cursor': 'pointer'
}

close_icon_styles_modal = {
    'color': 'white',
    'fontSize': '16px'
}
tooltip_styles = {
    'position': 'fixed',
    'top': '50%',
    'left': '50%',
    'transform': 'translate(-50%, -50%)',
    'backgroundColor': 'white',
    'padding': '20px',
    'borderRadius': '10px',
    'boxShadow': '0 2px 8px rgba(0,0,0,0.1)',
    'zIndex': '1000',
    'display': 'block'
}

close_button_styles = {
    'position': 'absolute',
    'top': '-8px',
    'right': '-8px',
    'width': '24px',
    'height': '24px',
    'borderRadius': '50%',
    'backgroundColor': 'rgba(209, 0, 0, 0.5)',
    'display': 'flex',
    'justifyContent': 'center',
    'alignItems': 'center',
    'cursor': 'pointer'
}

close_icon_styles = {
    'color': 'white',
    'fontSize': '16px'
}


def add_tip_box_to_content(tab, content, chart_descriptions, tip=""):
    description = chart_descriptions.get(tab, "")
    tip_box = html.Div([
        html.Div([
            html.H4("提示信息", style={'textAlign': 'center', 'margin': '0'}),
            html.P(description if not tip else tip),
            html.Div(
                html.Span('×', style=close_icon_styles),
                id=f'close-tip-{tab}',
                n_clicks=0,
                style=close_button_styles
            )
        ], style=tooltip_styles, id=f'tip-box-{tab}'),
        content
    ])
    return tip_box


app = dash.Dash(__name__, suppress_callback_exceptions=True)
df = data_cache.read_data()
if df is not None:
    poets = df['作者'].unique().tolist()
else:
    poets = []
cover_layout = html.Div(
    style={
        'position': 'fixed',
        'top': '0',
        'left': '0',
        'width': '100%',
        'height': '100%',
        'background': 'url("assets/background.jpg") no-repeat center center fixed',
        'backgroundSize': 'cover',
        'display': 'flex',
        'flexDirection': 'column',
        'alignItems': 'center',
        'justifyContent': 'center',
        'textAlign': 'center',
        'color': 'white'
    },
    children=[
        html.H1(
            "唐代诗词数字人文研究",
            style={
                'fontSize': '4em',
                'textShadow': '2px 2px 4px rgba(0,0,0,0.5)',
                'marginBottom': '20px',
                'fontFamily': 'SimHei'
            }
        ),
        html.Div(
            style={'maxWidth': '800px', 'margin': '0 auto'},
            children=[
                html.P(
                    "本平台整合《全唐诗》与历史地理数据，",
                    style={'fontSize': '1.5em', 'margin': '10px', 'fontFamily': 'SimHei'}
                ),
                html.P(
                    "通过多维度可视化展现唐代诗词中的自然意象、",
                    style={'fontSize': '1.5em', 'margin': '10px', 'fontFamily': 'SimHei'}
                ),
                html.P(
                    "诗人地域分布及文化传播路径。",
                    style={'fontSize': '1.5em', 'margin': '10px', 'fontFamily': 'SimHei'}
                )
            ]
        ),
        html.Div(
            style={
                'display': 'flex',
                'flexDirection': 'row',
                'gap': '30px',
                'marginTop': '40px'
            },
            children=[
                dcc.Link(
                    html.Button(
                        "开始探索 →",
                        style={
                            'padding': '15px 50px',
                            'fontSize': '1.5em',
                            'backgroundColor': '#FF6B6B',
                            'color': 'white',
                            'border': 'none',
                            'borderRadius': '30px',
                            'cursor': 'pointer',
                            'transition': 'all 0.3s ease',
                            'fontFamily': 'SimHei',
                            ':hover': {
                                'transform': 'scale(1.05)',
                                'boxShadow': '0 5px 15px rgba(255,107,107,0.4)'
                            }
                        }
                    ),
                    href='/analyze',
                    style={'textDecoration': 'none'}
                ),
                dcc.Link(
                    html.Button(
                        "诗词数据 →",
                        style={
                            'padding': '15px 50px',
                            'fontSize': '1.5em',
                            'backgroundColor': '#4CAF50',
                            'color': 'white',
                            'border': 'none',
                            'borderRadius': '30px',
                            'cursor': 'pointer',
                            'transition': 'all 0.3s ease',
                            'fontFamily': 'SimHei',
                            ':hover': {
                                'transform': 'scale(1.05)',
                                'boxShadow': '0 5px 15px rgba(76,175,80,0.4)'
                            }
                        }
                    ),
                    href='/poet-data',
                    style={'textDecoration': 'none'}
                )
            ]
        )
    ]
)


def get_main_layout():
    return html.Div([
        html.H1("唐代诗词数据分析仪表盘", style={'textAlign': 'center'}),
        dcc.Dropdown(
            id='province-filter',
            options=[{'label': v, 'value': k} for k, v in province_mapping.items()],
            multi=True,
            placeholder="选择省份(默认显示全部)",
            value=[]
        ),
        dcc.Dropdown(
            id='poet-filter',
            options=[{'label': poet, 'value': poet} for poet in poets],
            placeholder="选择诗人(默认显示全部)"
        ),
        dcc.Tabs(id="tabs", value='wordcloud', children=[
            dcc.Tab(label='词云图', value='wordcloud'),
            dcc.Tab(label='热力图', value='heatmap'),
            dcc.Tab(label='地图可视化', value='map'),
            dcc.Tab(label='雷达图', value='radar'),
            dcc.Tab(label='关系网络图', value='network_graph'),
            dcc.Tab(label='柱状图', value='bar_chart'),
            dcc.Tab(label='箱线图', value='box_plot'),
            dcc.Tab(label='桑基图', value='sankey_chart'),
            dcc.Tab(label='旭日图', value='sunburst_chart'),
            dcc.Tab(label='李白迁徙路径', value='migration')
        ]),
        html.Div(id='tab-content'),
        html.Div([
            html.P("数据来源说明：本仪表盘数据来源于《全唐诗》，《中国历代人物传记数据库》(CBDB)"),
        ], style={'textAlign': 'center', 'marginTop': '20px'}),
        html.Div(
            dcc.Link(
                html.Button(
                    "← 返回主界面",
                    style={
                        'padding': '10px 30px',
                        'backgroundColor': '#95a5a6',
                        'color': 'white',
                        'border': 'none',
                        'borderRadius': '20px',
                        'cursor': 'pointer',
                        'marginTop': '30px'
                    }
                ),
                href='/'
            ),
            style={'textAlign': 'center'}
        )
    ])


def get_poet_data_layout():
    merged_df = data_cache.read_data()
    return html.Div([
        html.Div(
            style={
                'padding': '20px',
                'position': 'relative'
            },
            children=[
                html.H1("唐代诗人诗词数据总览", style={'textAlign': 'center', 'color': '#2c3e50'}),
                html.Button(
                    '查看诗数量排名前五的诗人柱状图',
                    id='poet-poem-count-button',
                    style={
                        'position': 'absolute',
                        'top': '20px',
                        'right': '20px',
                        'padding': '12px 20px',
                        'backgroundColor': '#3498db',
                        'color': 'white',
                        'border': 'none',
                        'borderRadius': '25px',
                        'cursor': 'pointer',
                        'fontSize': '16px'
                    }
                ),
                html.Div(
                    style={
                        'display': 'flex',
                        'alignItems': 'center',
                        'gap': '10px'
                    },
                    children=[
                        dcc.Input(
                            id='search-input',
                            type='text',
                            placeholder='输入诗人或省份',
                            style={
                                'flex': '1',
                                'padding': '12px 20px',
                                'border': '2px solid #3498db',
                                'borderRadius': '25px',
                                'fontSize': '16px'
                            }
                        ),
                        html.Button(
                            '搜索',
                            id='search-button',
                            style={
                                'padding': '12px 20px',
                                'backgroundColor': '#3498db',
                                'color': 'white',
                                'border': 'none',
                                'borderRadius': '25px',
                                'cursor': 'pointer',
                                'fontSize': '16px'
                            }
                        )
                    ]
                ),
                dash.dash_table.DataTable(
                    id='poet-table',
                    columns=[
                        {"name": "诗人", "id": "作者"},
                        {"name": "性别", "id": "性别"},
                        {"name": "古代地址", "id": "古代地址"},
                        {"name": "现代地址", "id": "现代地址"},
                        {"name": "诗词名称", "id": "诗词名称"},
                        {"name": "诗词内容", "id": "诗词内容"}
                    ],
                    page_size=15,
                    style_table={
                        'borderRadius': '10px',
                        'overflow': 'hidden',
                        'boxShadow': '0 2px 15px rgba(0,0,0,0.1)'
                    },
                    style_header={
                        'backgroundColor': '#3498db',
                        'color': 'white',
                        'fontWeight': 'bold'
                    },
                    style_cell={
                        'padding': '12px',
                        'textAlign': 'left',
                        'border': '1px solid #f0f0f0'
                    },
                    style_data_conditional=[
                        {
                            'if': {'row_index': 'odd'},
                            'backgroundColor': '#f9f9f9'
                        }
                    ],
                    filter_action='native',
                    sort_action='native'
                ),
                html.Div(
                    id='poem-display',
                    style={
                        'marginTop': '30px',
                        'padding': '20px',
                        'backgroundColor': '#f8f9fa',
                        'borderRadius': '10px',
                        'boxShadow': '0 2px 8px rgba(0,0,0,0.1)'
                    }
                ),
                html.Div(
                    id='poet-poem-count-modal',
                    style={
                        'display': 'none',
                        'position': 'fixed',
                        'top': '50%',
                        'left': '50%',
                        'transform': 'translate(-50%, -50%)',
                        'backgroundColor': 'white',
                        'padding': '20px',
                        'borderRadius': '10px',
                        'boxShadow': '0 2px 8px rgba(0,0,0,0.1)',
                        'zIndex': '1000'
                    },
                    children=[
                        html.H3('诗数量排名前五的诗人柱状图', style={'textAlign': 'center'}),
                        dcc.Graph(id='poet-poem-count-chart'),
                        html.Button(
                            '详细分析',
                            id='detailed-analysis-button',
                            style={
                                'padding': '12px 20px',
                                'backgroundColor': '#3498db',
                                'color': 'white',
                                'border': 'none',
                                'borderRadius': '25px',
                                'cursor': 'pointer',
                                'fontSize': '16px',
                                'display': 'block',
                                'margin': '20px auto 0 auto'
                            }
                        )
                    ]
                ),
                html.Div(
                    dcc.Link(
                        html.Button(
                            "← 返回主界面",
                            style={
                                'padding': '10px 30px',
                                'backgroundColor': '#95a5a6',
                                'color': 'white',
                                'border': 'none',
                                'borderRadius': '20px',
                                'cursor': 'pointer',
                                'marginTop': '30px'
                            }
                        ),
                        href='/'
                    ),
                    style={'textAlign': 'center'}
                )
            ]
        )
    ])


@app.callback(
    [Output('poet-poem-count-modal', 'style', allow_duplicate=True),
     Output('poet-poem-count-modal', 'children')],
    [Input('poet-poem-count-button', 'n_clicks')],
    prevent_initial_call=True
)
def show_poet_poem_count_chart(n_clicks):
    df = data_cache.read_data()
    if df is None:
        error_msg = html.P("数据读取失败，请检查数据文件或格式。", style={'color': 'red'})
        return {'display': 'block'}, [error_msg]
    if n_clicks:
        poet_poem_count = df.groupby('作者').size().reset_index(name='诗的数量')
        top_five_poets = poet_poem_count.sort_values(by='诗的数量', ascending=False).head(5)
        fig = px.bar(
            top_five_poets,
            x='作者',
            y='诗的数量',
            title='诗数量排名前五的诗人柱状图',
            labels={'作者': '诗人', '诗的数量': '诗的数量'}
        )
        button = html.Button(
            '跳转到唐代诗词数据分析仪表图',
            id='top-five-poets-analysis-button',
            style={
                'padding': '12px 20px',
                'backgroundColor': '#3498db',
                'color': 'white',
                'border': 'none',
                'borderRadius': '25px',
                'cursor': 'pointer',
                'fontSize': '16px',
                'display': 'block',
                'margin': '20px auto 0 auto'
            }
        )
        modal_content = [
            html.H3('诗数量排名前五的诗人柱状图', style={'textAlign': 'center'}),
            dcc.Graph(id='poet-poem-count-chart', figure=fig),
            button,
            html.Div(
                html.Span('×', style=close_icon_styles_modal),
                id='close-modal-button',
                n_clicks=0,
                style=close_button_styles_modal
            )
        ]
        return {'display': 'block'}, modal_content
    return {'display': 'none'}, []


@app.callback(
    Output('poet-poem-count-modal', 'style', allow_duplicate=True),
    [Input('close-modal-button', 'n_clicks')],
    prevent_initial_call=True
)
def close_modal(n_clicks):
    style = modal_styles.copy()
    style['display'] = 'none' if n_clicks else 'block'
    return style


@app.callback(
    Output('url', 'pathname'),
    [Input('top-five-poets-analysis-button', 'n_clicks')]
)
def go_to_analysis_dashboard(n_clicks):
    if n_clicks:
        return '/analyze'
    return dash.no_update


@app.callback(
    [Output('poet-table', 'data'),
     Output('poet-table', 'page_current'),
     Output('poet-table', 'active_cell')],
    [Input('search-button', 'n_clicks'),
     Input('search-input', 'n_submit')],
    [State('search-input', 'value')]
)
def update_table(n_clicks, n_submit, search_text):
    df = data_cache.read_data()
    if df is None:
        return [], 0, None
    display_columns = ['作者', '性别', '古代地址', '现代地址', '诗词名称', '诗词内容']
    df = df[display_columns].copy()
    df['性别'] = df['性别'].fillna('未知')
    df['古代地址'] = df['古代地址'].fillna('地址不详')
    df['现代地址'] = df['现代地址'].fillna('地址不详')
    if search_text:
        search_text = search_text.lower()
        filtered = df[
            df['作者'].str.lower().str.contains(search_text) |
            df['诗词内容'].str.lower().str.contains(search_text) |
            df['现代地址'].str.lower().str.contains(search_text)
            ]
    else:
        filtered = df
    return filtered.to_dict('records'), 0, None


@app.callback(
    Output('poem-display', 'children'),
    [Input('poet-table', 'active_cell')],
    [State('poet-table', 'data'),
     State('poet-table', 'page_current'),
     State('poet-table', 'page_size')]
)
def display_poem(active_cell, data, page_current, page_size):
    if not active_cell or not data:
        return "请点击表格中的行查看诗词内容"
    if page_current is None:
        page_current = 0
    if page_size is None:
        page_size = 15
    total_index = page_current * page_size + active_cell['row']
    df_temp = pd.DataFrame(data)
    if total_index < 0 or total_index >= len(df_temp):
        return "无法找到对应的诗词内容，可能是数据分页问题，请刷新页面重试"
    row_data = df_temp.iloc[total_index]
    return html.Div([
        html.H4(f"{row_data.get('诗词名称', '未知诗名')} - {row_data.get('作者', '未知作者')}",
                style={'color': '#2c3e50', 'marginBottom': '15px'}),
        html.Div(
            row_data.get('诗词内容', '内容缺失'),
            style={
                'whiteSpace': 'pre-wrap',
                'lineHeight': '1.8',
                'fontSize': '16px',
                'maxHeight': '300px',
                'overflowY': 'auto',
                'padding': '15px',
                'backgroundColor': 'white',
                'borderRadius': '8px'
            }
        )
    ])


app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])


@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_page(pathname):
    if pathname == '/analyze':
        return get_main_layout()
    elif pathname == '/poet-data':
        return get_poet_data_layout()
    else:
        return cover_layout


@app.callback(Output('tab-content', 'children'),
              [Input('tabs', 'value'),
               Input('province-filter', 'value'),
               Input('poet-filter', 'value')])
def render_content(tab, selected_provinces, selected_poet):
    if tab == 'migration':
        libai_df = load_libai_data()
        if libai_df is None:
            return html.Div("李白迁徙数据加载失败，请检查数据文件")
        migration_content = html.Div([
            html.H3("李白生平迁徙路径", style={'textAlign': 'center'}),
            dcc.Graph(
                id='migration-map',
                figure=create_migration_figure(libai_df),
                style={'height': '70vh'}
            ),
            html.Div([
                html.Button('▶ 播放', id='play-button',
                            style={'backgroundColor': '#3498db', 'color': 'white'}),
                html.Button('⏸ 暂停', id='pause-button',
                            style={'marginLeft': '10px', 'backgroundColor': '#95a5a6'}),
                dcc.Slider(
                    id='time-slider',
                    min=0,
                    max=len(libai_df['时间'].unique()) - 1,
                    value=0,
                    marks={i: {'label': f"{int(t)}"}
                           for i, t in enumerate(sorted(libai_df['时间'].unique())) if i % 2 == 0},
                    tooltip={'placement': 'bottom'}
                ),
                dcc.Interval(id='auto-play', interval=1500, disabled=True)
            ], style={'marginTop': '20px'})
        ])
        return add_tip_box_to_content('migration', migration_content, chart_descriptions)
    df = data_cache.read_data()
    if df is None:
        return html.Div("数据加载或处理出错")
    tip = ""
    if isinstance(selected_provinces, list):
        selected_provinces = tuple(selected_provinces)
    if isinstance(selected_poet, list):
        selected_poet = tuple(selected_poet)
    elif isinstance(selected_poet, str):
        selected_poet = (selected_poet,)
    if selected_provinces:
        province_filtered_df = df[df['省份'].isin(selected_provinces)]
        if province_filtered_df.empty:
            tip = f"所选省份 {', '.join(selected_provinces)} 没有数据。"
            return html.Div(tip)
        else:
            df = province_filtered_df
    if selected_poet:
        poet_filtered_df = df[df['作者'].isin(selected_poet)]
        if poet_filtered_df.empty:
            tip = f"所选诗人 {', '.join(selected_poet)} 没有数据。"
            return html.Div(tip)
        else:
            has_nature_images = poet_filtered_df['自然词汇'].apply(lambda x: len(x) > 0).any()
            if not has_nature_images:
                tip = f"所选诗人 {', '.join(selected_poet)} 均没有自然意象。"
                return html.Div(tip)
            else:
                df = poet_filtered_df
                if '自然词汇' not in df.columns or '省份' not in df.columns:
                    return html.Div(f"所选诗人 {', '.join(selected_poet)} 的数据缺少必要列，无法处理")
    all_nature_words = [word for sublist in df['自然词汇'] for word in sublist]
    word_freq = pd.Series(all_nature_words).value_counts().reset_index()
    word_freq.columns = ['词汇', '频次']
    if tab == 'wordcloud':
        word_freq_str = word_freq.to_json()
        content = dcc.Graph(figure=generate_wordcloud(word_freq_str))
        return add_tip_box_to_content('wordcloud', content, chart_descriptions, tip)
    elif tab == 'heatmap':
        df_str = df.to_json()
        content = dcc.Graph(figure=generate_heatmap(df_str))
        return add_tip_box_to_content('heatmap', content, chart_descriptions, tip)
    elif tab == 'map':
        df_str = df.to_json()
        content = generate_map(df_str)
        return add_tip_box_to_content('map', content, chart_descriptions, tip)
    elif tab == 'radar':
        content = dcc.Graph(figure=generate_radar(selected_poet))
        return add_tip_box_to_content('radar', content, chart_descriptions, tip)
    elif tab == 'network_graph':
        df_str = df.to_json()
        content = dcc.Graph(figure=async_generate_network_graph(df_str))
        return add_tip_box_to_content('network_graph', content, chart_descriptions, tip)
    elif tab == 'bar_chart':
        word_freq_str = word_freq.to_json()
        content = dcc.Graph(figure=generate_bar_chart(word_freq_str, selected_poet))
        return add_tip_box_to_content('bar_chart', content, chart_descriptions, tip)
    elif tab == 'box_plot':
        df_str = df.to_json()
        content = dcc.Graph(figure=generate_box_plot(df_str))
        return add_tip_box_to_content('box_plot', content, chart_descriptions, tip)
    elif tab == 'sankey_chart':
        df_str = df.to_json()
        content = dcc.Graph(figure=generate_sankey_chart(df_str))
        return add_tip_box_to_content('sankey_chart', content, chart_descriptions, tip)
    elif tab == 'sunburst_chart':
        df_str = df.to_json()
        content = dcc.Graph(figure=generate_sunburst_chart(df_str))
        return add_tip_box_to_content('sunburst_chart', content, chart_descriptions, tip)


for tab in chart_descriptions.keys():
    @app.callback(
        Output(f'tip-box-{tab}', 'style'),
        Input(f'close-tip-{tab}', 'n_clicks')
    )
    def close_single_tip_box(n_clicks):
        style = tooltip_styles.copy()
        style['display'] = 'none' if n_clicks else 'block'
        return style


@app.callback(
    Output('poet-filter', 'options'),
    [Input('province-filter', 'value')]
)
def update_poet_options(selected_provinces):
    df = data_cache.read_data()
    if df is None:
        return []
    if selected_provinces:
        province_filtered_df = df[df['省份'].isin(selected_provinces)]
        poets = province_filtered_df['作者'].unique().tolist()
        return [{'label': poet, 'value': poet} for poet in poets]
    else:
        poets = df['作者'].unique().tolist()
        return [{'label': poet, 'value': poet} for poet in poets]


@app.callback(
    [Output('migration-map', 'figure'),
     Output('time-slider', 'value'),
     Output('auto-play', 'disabled')],
    [Input('auto-play', 'n_intervals'),
     Input('time-slider', 'value'),
     Input('play-button', 'n_clicks'),
     Input('pause-button', 'n_clicks')],
    [State('auto-play', 'disabled'),
     State('tabs', 'value')]
)
def update_migration(n, slider_val, play, pause, is_disabled, current_tab):
    if current_tab != 'migration':
        return [dash.no_update] * 3
    libai_df = load_libai_data()
    if libai_df is None:
        return [go.Figure(), 0, True]
    ctx = dash.callback_context
    if not ctx.triggered:
        return [dash.no_update] * 3
    trigger_id = ctx.triggered[0]['prop_id'].split('.')[0]
    libai_df = load_libai_data()
    if libai_df is None:
        return [dash.no_update] * 3
    max_frame = len(libai_df['时间'].unique()) - 1
    if trigger_id == 'play-button':
        return [create_migration_figure(libai_df, slider_val), slider_val, False]
    if trigger_id in ['pause-button', 'time-slider']:
        return [create_migration_figure(libai_df, slider_val), slider_val, True]
    if trigger_id == 'auto-play':
        new_val = slider_val + 1 if slider_val < max_frame else 0
        return [create_migration_figure(libai_df, new_val), new_val, is_disabled]
    return [dash.no_update] * 3


if __name__ == '__main__':
    app.run_server(debug=True)
